-
-
Notifications
You must be signed in to change notification settings - Fork 336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow importing the generated XML in a DOMDocument and improving HTML converter output #348
Conversation
@kusabi thanks for the PR I see a great deal of thoughts has been put into it so really I appreciate it. I have some reservations on some UX decision made:
So what I would proposed instead and in line with my remarks on #344 is the following public API changed: <?php
HTMLConverter::convert(iterable $records, array $headerRecord = []): string On usage it would look like this <?php
$reader = Reader::createFromString($csvString);
$reader->setHeaderOffset(0);
$converter = (new HTMLConverter())
->table('table-csv-data', 'users')
->tr('data-record-offset')
->td('title')
;
$html = $converter->convert($reader, $reader->getHeader()); And if <table class="table-csv-data" id="users">
<thead>
<tr>
<th scope="col">firstname</td>
<th scope="col">lastname</td>
<th scope="col">email</td>
</tr>
</thead>
<tbody>
<tr data-record-offset="0">
<td title="firstname">john</td>
<td title="lastname">doe</td>
<td title="email">[email protected]</td>
</tr>
<tr data-record-offset="1">
<td title="firstname">jane</td>
<td title="lastname">doe</td>
<td title="email">[email protected]</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="col">firstname</td>
<th scope="col">lastname</td>
<th scope="col">email</td>
</tr>
</tfoot> What do you think ? |
That's sounds fine, I will look into a solution after work. My only concern with this approach is that without any modification to the XML converter, the HTML converter is going to have to get a little hacky. If you are OK with this I will look into your proposed approach. |
Ok. So attempt number 2. It is a little more concise this way and not nearly as hacky as I though it was going to get. What do you think? |
@kusabi I took the liberty to research the HTML5 and DOM spec and also what is capable or not in PHP regarding what we are trying to do. Here's what I did find:
So I came to this conclusion that a better implementation would be to add one public method to XMLConverter::import(iterable $records, DOMDocument $doc): DOMElement; The import method creates a Once this method is extracted/implemented then adding the new functionnality to the HTMLConverter::convert(iterable $records, array $headerRecord = []): string And here's a pseudo code public function convert(iterable $records, array $headerRecord = []): string
{
if ([] === $headerRecords) {
// current implementation does not change
}
$doc = new DOMDocument('1.0');
$thead = $this->createHeaders('thead', $headerRecord, $doc);
$tfoot = $this->createHeaders('tfoot', $headerRecord, $doc);
$tbody = $this->xml_converter->rootElement('tbody')->import($records, $doc);
$table = $doc->createElement('table');
$table->setAttribute('class', $this->class_name);
$table->setAttribute('id', $this->id_value);
$table->appendChild($thead);
$table->appendChild($tfoot);
$table->appendChild($tbody);
$doc->appendChild($table);
return $doc->saveHTML($table);
}
private function createHeaders(string $tagName, array $record, DOMDocument $doc): DOMElement
{
$converter = (new XMLConverter())
->rootElement($tagName)
->recordElement('tr')
->fieldElement('th');
$node = $converter->import([$record], $doc);
foreach ($node->getElementsByTagName('th') as $th) {
$th->setAttribute('scope', 'col');
}
return $node;
} IMHO this is much easier to understand and to implement and we open the door for those who want a different implementation to do it as they wish using the XMLConverter` object. |
No problem, switched to your proposed solution. If any headers or footers are added then the table has a I also made the footer and headers optional to each other (have one without the other). Let me know what you think. |
src/HTMLConverter.php
Outdated
$table = $doc->createElement('table'); | ||
$table = $this->styleTableElement($table); | ||
|
||
$table = $this->styleTableElement($table); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added twice 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doh! removed
src/HTMLConverter.php
Outdated
* | ||
* @return DOMElement | ||
*/ | ||
private function createRecordRow(string $rowTagName, string $elementTagName, array $record, DOMDocument $doc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can typehint the return type directly thanks PHP7 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added the PHP7 hint but not removed the phpdock hint as I was not sure if you implied that or not?
src/HTMLConverter.php
Outdated
{ | ||
/** @var DOMDocument $doc */ | ||
$doc = $this->xml_converter->convert($records); | ||
// If we do not want to add any headers or records, then we can default entirely to the XML converter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is unnecessary the code is self explaining in itself
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
* | ||
* @return DOMElement | ||
*/ | ||
private function styleTableElement(DOMElement $table_element) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return typed whenever you can
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
->tr('data-record-offset') | ||
; | ||
|
||
$html = $converter->convert($records, $records->getHeader()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you only provide a thead
why does the tfoot
is present 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I assumed that they came as a threesome...
I originally hid them accordingly, would you you prefer I did that again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes hides them accordingly ... I prefer this to be explicit. I was unable to find a resource stating for instance that if you use tfoot
then thead
is required. The only requirement is that if one is present then tbody
must be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
$doc = new DOMDocument('1.0'); | ||
$element = $converter->import($records, $doc); | ||
|
||
self::assertInstanceOf(DOMDocument::class, $doc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/HTMLConverter.php
Outdated
* | ||
* @return DOMElement | ||
*/ | ||
private function createRecordRow(string $rowTagName, string $elementTagName, array $record, DOMDocument $doc) : DOMElement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename
$rowTagName
to$recordTagName
$elementTagName
to$fieldTagName
for consistency in naming with the reste of the package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renamed
src/HTMLConverter.php
Outdated
* | ||
* @return DOMElement | ||
*/ | ||
private function styleTableElement(DOMElement $table_element) : DOMElement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think styleTableElement
needs to return anything the object is already known. 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
… output of HTMLConverter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll accept the PR as is since I believe it's feature complete and I'll add your contribution in the next release but I reserved the right to change some small implementation details if needed. But already big thanks to your contribution 🥇
@kusabi here's the changes I made to your code after the PR was merged:
I hope I'll be able to release the new version |
In reference to #344
Added an optional parameter to the
HTMLConverter::convert($records, $headers = null)
method.When an array of headers is passed, the HTML table will contain a row of
<th>
elements.The code in question goes from...
and becomes...